﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows;
using System.Drawing;
using System.Data;

namespace SkiPathAnalysis
{
    class SkiPathAnalysis : MapWindow.Interfaces.IPlugin
    {
        #region PublicDeclaration
        public static MapWindow.Interfaces.IMapWin g_MapWin;
        public Stack<string> g_MenuStack = new Stack<string>();
        //(1) Create an integer variable to store the raster grid layer handler
        //public int intHandler;
        //(2) Create an instance for the grid raster 
        public MapWinGIS.Grid myGrid = new MapWinGIS.Grid();
        //(3) Create a new grid header object
        public MapWinGIS.GridHeader myHeader = new MapWinGIS.GridHeader();
        //(4) Create an image instance to store and display the raster
        public MapWinGIS.Image myImage = new MapWinGIS.Image();
        public static DataTable table = new DataTable();
        #endregion

        #region PrivateDeclaration
        private bool g_boolDraw = false;
        private double g_lastStartX  = -1;
        private double g_lastStartY = -1;
        private List<MapWinGIS.Point> g_listOfPoints = new List<MapWinGIS.Point>();
        private Int32 g_drawingHandle = -1;
        private double g_curX  = -1;
        private double g_curY  = -1;
        private int demHandle;

        #endregion

        #region IPlugin Members

        public string Author
        {
            get { return "YANG CAO" ; }
        }

        public string BuildDate
        {
            get { return "cdm"; }
        }

        public string Description
        {
            get { return "SkiPathAnalysis"; }
        }

        public void Initialize(MapWindow.Interfaces.IMapWin MapWin, int ParentHandle)
        {
            //MessageBox.Show("The c# sample plugin has been initialized.");
            g_MapWin = MapWin;

            g_MapWin.Menus.AddMenu("mfpMain", "", "SkiPathAnalysis", "");
            g_MenuStack.Push("mfpMain");

            g_MapWin.Menus.AddMenu("mfpLoadDEM", "mfpMain", "Load DEM", "");
            g_MenuStack.Push("mfpLoadDEM");

            g_MapWin.Menus.AddMenu("mfpDrawPath", "mfpMain", "Draw Path", "");
            g_MenuStack.Push("mfpDrawPath");

            g_MapWin.Menus.AddMenu("mfpGenerateProfile", "mfpMain", "Generate Profile", "");
            g_MenuStack.Push("mfpGenerateProfile");

            g_MapWin.Menus.AddMenu("mfpDifficultyCalculation", "mfpMain", "Difficulty Calculation", "");
            g_MenuStack.Push("mfpDifficultyCalculation");

        }

        public void ItemClicked(string ItemName, ref bool Handled)
        {
           // throw new NotImplementedException();
            MapWinGIS.Shapefile myWorldShape = new MapWinGIS.Shapefile();
            switch (ItemName)
            {
                case ("mfpLoadDEM"):
                    LoadDEMData();            
                    //Handled = true;
                    break;
                case ("mfpDrawPath"):
                    g_MapWin.View.CursorMode = MapWinGIS.tkCursorMode.cmNone;
                    g_MapWin.View.MapCursor = MapWinGIS.tkCursor.crsrCross;
                    break;
                case ("mfpGenerateProfile"):
                    GenerateProfile();
                    break;
                case ("mfpDifficultyCalculation"):
                    frm_DifficultyAnalysis frm = new frm_DifficultyAnalysis();
                    frm.Show();
                    break;
            }

        }

        public void LayerRemoved(int Handle)
        {
           // throw new NotImplementedException();
        }

        public void LayerSelected(int Handle)
        {
            //throw new NotImplementedException();
        }

        public void LayersAdded(MapWindow.Interfaces.Layer[] Layers)
        {
            //throw new NotImplementedException();
        }

        public void LayersCleared()
        {
           // throw new NotImplementedException();
        }

        public void LegendDoubleClick(int Handle, MapWindow.Interfaces.ClickLocation Location, ref bool Handled)
        {
            //throw new NotImplementedException();
        }

        public void LegendMouseDown(int Handle, int Button, MapWindow.Interfaces.ClickLocation Location, ref bool Handled)
        {
           // throw new NotImplementedException();
        }

        public void LegendMouseUp(int Handle, int Button, MapWindow.Interfaces.ClickLocation Location, ref bool Handled)
        {
            //throw new NotImplementedException();
        }

        public void MapDragFinished(System.Drawing.Rectangle Bounds, ref bool Handled)
        {
            //throw new NotImplementedException();
        }

        public void MapExtentsChanged()
        {
            //throw new NotImplementedException();
        }

        public void MapMouseDown(int Button, int Shift, int x, int y, ref bool Handled)
        {
            //Button number 2 means right click
            if((g_boolDraw == true) &&(Button == 2))
            {
            //convert screen coordinates to map coordinates
                g_MapWin.View.PixelToProj(x, y, ref g_lastStartX, ref g_lastStartY);
               //add the point to the list of points
                MapWinGIS.Point endPoint = new MapWinGIS.PointClass();
                endPoint.x = g_lastStartX;
                endPoint.y = g_lastStartY;
                g_listOfPoints.Add(endPoint);
                MessageBox.Show("There are " + g_listOfPoints.Count + " Points in the Ski Path!", "Finish Editing",
               MessageBoxButtons.OK,MessageBoxIcon.Information,MessageBoxDefaultButton.Button1);
               // MessageBox.Show("There are "+ g_listOfPoints.Count + "Poiints in the Ski Path !");
                g_boolDraw = false;
                return;
            }
            //TODO save points as shapefile and add to map
            if(g_boolDraw==false)
            {
                g_boolDraw = true;
            }
            g_drawingHandle = g_MapWin.View.Draw.NewDrawing(MapWinGIS.tkDrawReferenceList.dlSpatiallyReferencedList);

            //convert screen coordinates to map coordinates
            g_MapWin.View.PixelToProj(x, y, ref g_lastStartX, ref g_lastStartY);

            //add the point to the list of points
            MapWinGIS.Point curPoint = new MapWinGIS.PointClass();
            curPoint.x = g_lastStartX;
            curPoint.y = g_lastStartY;
            g_listOfPoints.Add(curPoint);

        }

        public void MapMouseMove(int ScreenX, int ScreenY, ref bool Handled)
        {
            if(g_boolDraw ==false)
            {
               return;
            }
            g_MapWin.View.PixelToProj(ScreenX, ScreenY, ref g_curX, ref g_curY);
            //clear previously drawn line and draw a new line on the map
            g_MapWin.View.Draw.ClearDrawing(g_drawingHandle);
            g_drawingHandle = g_MapWin.View.Draw.NewDrawing(MapWinGIS.tkDrawReferenceList.dlSpatiallyReferencedList);
            g_MapWin.View.Draw.DrawLine(g_lastStartX, g_lastStartY, g_curX, g_curY, 2, System.Drawing.Color.Blue);
        }

        public void MapMouseUp(int Button, int Shift, int x, int y, ref bool Handled)
        {
            //throw new NotImplementedException();
        }

        public void Message(string msg, ref bool Handled)
        {
            //throw new NotImplementedException();
        }

        public string Name
        {
            get { return "SkiPathAnalysis"; }
        }

        public void ProjectLoading(string ProjectFile, string SettingsString)
        {
            //throw new NotImplementedException();
        }

        public void ProjectSaving(string ProjectFile, ref string SettingsString)
        {
           // throw new NotImplementedException();
        }

        public string SerialNumber
        {
            get { return "Caoyang"; }
        }

        public void ShapesSelected(int Handle, MapWindow.Interfaces.SelectInfo SelectInfo)
        {
          //  throw new NotImplementedException();
        }

        public void Terminate()
        {
           // throw new NotImplementedException();
            g_MapWin.Menus.Remove(g_MenuStack.Pop());
        }

        public string Version
        {
            get { return "Caoyang"; }
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        private void LoadDEMData()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "dem|*.dem";
            if (ofd.ShowDialog() != DialogResult.OK) return;
            try
            {
                myGrid.Open(ofd.FileName,MapWinGIS.GridDataType.DoubleDataType,
                    false, MapWinGIS.GridFileType.GeoTiff,null);
                MapWinGIS.GridColorScheme myScheme =
                    new MapWinGIS.GridColorScheme();
                myScheme.NoDataColor =
                        System.Convert.ToUInt32(
                        System.Drawing.ColorTranslator.ToOle( //
                        System.Drawing.Color.FromArgb(0, 0, 0)));
                myScheme.UsePredefined(
                        (double)myGrid.Minimum,
                        (double)myGrid.Maximum,
                        MapWinGIS.PredefinedColorScheme.DeadSea);
                string gridName=ofd.FileName;
                MapWindow.Interfaces.Layer g_demLayer = g_MapWin.Layers.Add(ref myGrid, myScheme);
                demHandle = g_demLayer.Handle;
                myGrid = g_demLayer.GetGridObject;
                //myHeader = myGrid.Header;
                
                g_MapWin.View.CursorMode = MapWinGIS.tkCursorMode.cmNone;
            }
            catch { }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="startX"></param>
        /// <param name="startY"></param>
        /// <param name="endX"></param>
        /// <param name="endY"></param>
        /// <param name="demGrid"></param>
        /// <param name="table"></param>
        private void FactorCalulator(double startX , double startY , double endX , double endY , 
                                      MapWinGIS.Grid demGrid, DataTable table )
        {
             //get the cell size of the dem Grid
            double cellX  = demGrid.Header.dX;
            double cellY  = demGrid.Header.dY;
            int startRow;
            int endRow ;
            int startCol;
            int endCol;
            int curRow ;
            int curCol ;
            double curDist = 0;

            demGrid.ProjToCell(startX, startY, out startCol, out startRow);
            demGrid.ProjToCell(endX, endY, out endCol, out endRow);

            //Get the Height difference
            //we assume the number of points in the segment is 100 points
            Int32 numberOfPoints  = 3;

            //declare arrays for storing distance, elevation, slope
            double[] distanceArray = new double[numberOfPoints-1];
            double[] elevationArray = new double[numberOfPoints-1];
            double[] slopeArray = new double[numberOfPoints-1];

            double constXDif =(endCol - startCol) / numberOfPoints;
            double constYDif =(endRow - startRow) / numberOfPoints;
            double constDiagDif=Math.Sqrt(Math.Pow(constXDif,2.0) + Math.Pow(constYDif,2.0));
            curCol = startCol;
            curRow = startRow;
            distanceArray[0] = curDist;
            elevationArray[0] = Convert.ToDouble(demGrid.get_Value(curCol, curRow));
            for(int j=1;j<numberOfPoints-1;j++)
            {
                curCol = curCol + Convert.ToInt32(constXDif);
                curRow = curRow + Convert.ToInt32(constYDif);
                curDist = curDist + constDiagDif;
                distanceArray[j] = curDist;
                elevationArray[j] =Convert.ToDouble(demGrid.get_Value(curCol, curRow));
            }
            //find the slope
            for(int k=0;k<distanceArray.Length;k++)
            {
                if(k == distanceArray.Length - 1)
                {
                    slopeArray[k] = slopeArray[k - 1];
                    break;
                }
                double elevationDif = Math.Abs(elevationArray[k] - elevationArray[k + 1]);
                double slope  = elevationDif / constDiagDif;
                slopeArray[k] = slope;
            }
 
            //Assign the data to a table for display
            if(table.Rows.Count == 0)
            {
                for (int i = 0 ; i < elevationArray.Length; i++)
                {
                    DataRow tableRow = table.NewRow();
                    tableRow[0] = distanceArray[i];
                    tableRow[1] = elevationArray[i];
                    tableRow[2] = slopeArray[i];
                    table.Rows.Add(tableRow);
                }
            }
            else
            {
                double initialDistance  = Convert.ToDouble(table.Rows[table.Rows.Count-1].ItemArray[0].ToString());
                for (int i = 1; i < elevationArray.Length ; i++)
                {
                    DataRow tableRow = table.NewRow();
                    tableRow[0] = distanceArray[i]+ initialDistance;
                    tableRow[1] = elevationArray[i];
                    tableRow[2] = slopeArray[i];
                    table.Rows.Add(tableRow);
                }
            }
        }
        private void GenerateProfile()
        {
            if (g_listOfPoints.Count == 0)
            {
                MessageBox.Show("Please Draw A Ski Route.");
            }
            else
            {
                //after drawing the line, extract the elevation along the line
                MapWindow.Interfaces.Layer demLayer = g_MapWin.Layers[demHandle];
                MapWinGIS.Grid DemGrid = demLayer.GetGridObject;

                table.Columns.Add(new DataColumn("distance"));
                table.Columns.Add(new DataColumn("elevation"));
                table.Columns.Add(new DataColumn("slope"));
                for (int i = 0; i < g_listOfPoints.Count; i++)
                {
                    if (i == g_listOfPoints.Count - 1) break;
                    FactorCalulator(g_listOfPoints[i].x, g_listOfPoints[i].y,
                        g_listOfPoints[i + 1].x, g_listOfPoints[i + 1].y, DemGrid, table);
                }
                frm_DrawProfile frm = new frm_DrawProfile();
                frm.Show();
            }
        }

    }
}
